package com.thoughtservice.jspdoc.executors;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtservice.jspdoc.JSPFileList;
import com.thoughtservice.jspdoc.JSPPage;
import com.thoughtservice.jspdoc.output.GenerateHtmlStructure;
import com.thoughtservice.jspdoc.output.JSPPageEnum;
import com.thoughtservice.jspdoc.processors.DocProcessor;
import com.thoughtservice.jspdoc.processors.ProcessAllParams;
import com.thoughtservice.jspdoc.processors.ProcessDynamicIncludes;
import com.thoughtservice.jspdoc.processors.ProcessErrorPage;
import com.thoughtservice.jspdoc.processors.ProcessForm;
import com.thoughtservice.jspdoc.processors.ProcessLinksTo;
import com.thoughtservice.jspdoc.processors.ProcessLinksToForm;
import com.thoughtservice.jspdoc.processors.ProcessPageDescription;
import com.thoughtservice.jspdoc.processors.ProcessPageImports;
import com.thoughtservice.jspdoc.processors.ProcessRedirects;
import com.thoughtservice.jspdoc.processors.ProcessStaticIncludes;
import com.thoughtservice.jspdoc.processors.ProcessTileInserts;
import com.thoughtservice.jspdoc.processors.ProcessTileInsertsIn;

/**
 * Major chunk of report generation passed to the executor workhorse. Each
 * processor is executed in diffrent thread
 * 
 * @author Shinu Suresh
 * 
 */
public class JSPDocExecutor {

	private static Logger LOG = LoggerFactory.getLogger(JSPDocExecutor.class);

	private JSPPage page;
	private String writeToPage;
	private JSPFileList fileList;
	private String rootSourceDir;
	
	private static Set<Class<? extends DocProcessor>> processors;	
	
	static{
		//Figure out all Processors and execute
		Reflections reflections = new Reflections("com.thoughtservice.jspdoc");
		 
		processors = reflections.getSubTypesOf(DocProcessor.class);
	}

	public JSPDocExecutor(final JSPPage page, final JSPFileList fileList,
			final String writeToPage, final String rootSourceDir) {
		this.page = page;
		this.writeToPage = writeToPage;
		this.fileList = fileList;
		this.rootSourceDir = rootSourceDir;
		
	}

	public JSPDocExecutor() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * Process summaries write to file operation in a threaded manner
	 * 
	 * @return
	 */
	public List<Future<Object>> processOutputSummaries(final GenerateHtmlStructure htmlStructure) {
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		Set<Callable<Object>> callables = new HashSet<Callable<Object>>();

		// Execute all processing tasks

		// Ouput Summaries
		callables.add(new Callable<Object>() {
			public Object call() throws Exception {
				return htmlStructure.outputSummaries();
			}
		});
		
		// Build the index by page name		
		callables.add(new Callable<Object>() {
			public Object call() throws Exception {
				return htmlStructure.outputIndexByPageName();
			}
		});
		
		// Build the index by directory name
		callables.add(new Callable<Object>() {
			public Object call() throws Exception {
				return htmlStructure.outputIndexByDirName();
			}
		});

		try {
			List<Future<Object>> futures = executorService.invokeAll(callables);
			/*
			 * for(Future<Object> future : futures){ LOG.info("future.get = " +
			 * future.get()); }
			 */
			executorService.shutdown();

			return futures;

		} catch (InterruptedException e) {
			LOG.error(ExceptionUtils.getFullStackTrace(e));
		}/*
		 * catch (ExecutionException e) {
		 * LOG.error(ExceptionUtils.getFullStackTrace(e)); }
		 */

		return null;
	}

	/**
	 * Processors for the page is threaded
	 * @return
	 */
	public List<Future<Object>> processExecutors() {
		ExecutorService executorService = Executors
				.newFixedThreadPool(JSPPageEnum.values().length);

		Set<Callable<Object>> callables = new HashSet<Callable<Object>>();

		// Execute all processing tasks
		
		for (final Class<? extends DocProcessor> processor : processors) {			
		    callables.add(new Callable<Object>() {
				public Object call() throws Exception {
					LOG.info("Processor {} executing.", processor.getName());
					Method method = processor.getMethod("process", JSPPage.class, String.class, JSPFileList.class);
					return method.invoke(processor.newInstance(), page, writeToPage, fileList);
					//return new ProcessPageImports(page, writeToPage);
				}
			});
		}

		/*// Invoke the Page Imports
		callables.add(new Callable<Object>() {
			public Object call() throws Exception {
				return new ProcessPageImports(page, writeToPage);
			}
		});

		// Get errorPage
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessErrorPage(page, writeToPage, fileList);
			}
		});

		// Look for page description
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessPageDescription(page, writeToPage);
				//return new ProcessAllParams(page, writeToPage);
			}
		});
		

		// Look for parameters
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessAllParams(page, writeToPage);
			}
		});

		// Look for LinksTo
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessLinksTo(page, writeToPage, fileList);
			}
		});

		// Look for LinksToForm
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessLinksToForm(page, writeToPage);
			}
		});

		// Look for <form
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessForm(page, writeToPage);
			}
		});

		// Look for Static Includes
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessStaticIncludes(page, writeToPage, fileList);
			}
		});

		// Look for Dynamic Includes
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessDynamicIncludes(page, writeToPage, fileList);
			}
		});

		// Look for redirects
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessRedirects(page, writeToPage);
			}
		});

		// Look for tiles:insert
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessTileInserts(page, writeToPage, fileList,
						rootSourceDir);
			}
		});

		// Look for tiles:insert in current JSP page
		callables.add(new Callable<Object>() {
			// Invoke the Page Imports
			public Object call() throws Exception {
				return new ProcessTileInsertsIn(page, writeToPage, fileList,
						rootSourceDir);
			}
		});*/

		try {
			List<Future<Object>> futures = executorService.invokeAll(callables);
			/*
			 * for(Future<Object> future : futures){ LOG.info("future.get = " +
			 * future.get()); }
			 */
			executorService.shutdown();

			return futures;

		} catch (InterruptedException e) {
			LOG.error(ExceptionUtils.getFullStackTrace(e));
		}/*
		 * catch (ExecutionException e) {
		 * LOG.error(ExceptionUtils.getFullStackTrace(e)); }
		 */

		return null;
	}
}
